#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from __future__ import annotations

import json
import re
from jsonpath_ng import parse as jsonpath_parse

from autoapi.common.errors import RequestDataParseError, VariableError
from autoapi.common.log import log
from autoapi.common.variable_cache import variable_cache
from autoapi.common.yaml_handler import read_yaml
from autoapi.core.get_conf import autoapi_config
from autoapi.core.path_conf import autoapi_path
from autoapi.utils.request.vars_recorder import record_variables


class VarsExtractor:
    def __init__(self) -> None:
        # 变量通用规则：
        # 1. 开头必须为 a-zA-Z_
        # 2. 变量有 {} 包住时，允许变量值前后存在任何内容，相反，则不允许变量值前后存在任何内容
        # 变量表达: ${var} 或 $var
        self.vars_re = re.compile(r"\${([a-zA-Z_]\w*)}|(?<!\S)\$([a-zA-Z_]\w*)(?!\S)")
        # 关联变量表达: ^{var} 或 ^var
        self.relate_vars_re = re.compile(
            r"\^{([a-zA-Z_]\w*)}|(?<!\S)\^([a-zA-Z_]\w*)(?!\S)"
        )

    def vars_replace(self, target: str | dict, env: str) -> str | dict:
        """
        变量替换

        :param target:
        :param env:
        :return:
        """
        if isinstance(target, dict):
            str_target = json.dumps(target, ensure_ascii=False)
        else:
            str_target = target

        # 获取环境变量字典
        if not env or not isinstance(env, str):
            raise RequestDataParseError(
                "运行环境获取失败, 测试用例数据缺少 config:request:env 参数"
            )
        
        # 从新的配置系统获取环境变量
        try:
            env_vars = autoapi_config.get_env_config(env)
        except Exception:
            raise RequestDataParseError("运行环境获取失败, 请检查测试用例环境配置")

        def replace_var(match):
            var_key = match.group(1) or match.group(2)
            if var_key is not None:
                try:
                    # 首先尝试获取无标签变量
                    cache_value = variable_cache.get(var_key)

                    # 如果无标签变量不存在，尝试获取关联测试用例标签的变量
                    if cache_value is None:
                        cache_value = variable_cache.get(var_key, tag="relate_testcase")

                    # 如果缓存中找到了变量
                    if cache_value is not None:
                        # 检查是否为列表形式的字符串（如 "[1, 2, 3]"）
                        if isinstance(cache_value, str) and cache_value.startswith('[') and cache_value.endswith(']'):
                            try:
                                # 解析为列表
                                import ast
                                value_list = ast.literal_eval(cache_value)
                                if isinstance(value_list, list) and len(value_list) > 1:
                                    # 如果是列表且包含多个值，返回第一个值并记录警告
                                    log.warning(f"🧩 变量 {var_key} 包含多个值，将使用第一个值: {value_list[0]}")
                                    return str(value_list[0])
                            except (ValueError, SyntaxError):
                                # 如果解析失败，按原样处理
                                pass
                        
                        log.info(f"🧩 变量替换成功: {var_key} --> {cache_value}")
                        return str(cache_value)

                    # 缓存中没有，尝试从环境变量/全局变量获取
                    global_vars = read_yaml(
                        autoapi_path.global_var_dir, filename="global_vars.yaml"
                    )
                    var_value = env_vars.get(
                        var_key.upper(),
                        global_vars.get(var_key) if global_vars else None,
                    )
                    if var_value is None:
                        raise VariableError(var_key)
                    log.info(f"🧩 变量替换成功: {var_key} --> {var_value}")
                    return str(var_value)
                except Exception as e:
                    raise VariableError(f"🧩 变量 {var_key} 替换失败: {str(e)}")
            
            return match.group(0)

        str_target = self.vars_re.sub(replace_var, str_target)

        return json.loads(str_target) if isinstance(target, dict) else str_target

    def relate_vars_replace(self, target: dict) -> dict:
        """
        关联变量替换

        :param target:
        :return:
        """
        str_target = json.dumps(target, ensure_ascii=False)

        match = self.relate_vars_re.search(str_target)
        if not match:
            return target

        var_keys = []
        log.info("执行关联测试用例变量替换")
        for match in self.relate_vars_re.finditer(str_target):
            var_key = match.group(1) or match.group(2)
            if var_key is not None:
                var_keys.append(var_key)
                default = "`AE86`"
                cache_value = variable_cache.get(
                    var_key, default=default, tag="relate_testcase"
                )
                if cache_value != default:
                    try:
                        str_target = self.relate_vars_re.sub(
                            str(cache_value), str_target, 1
                        )
                        log.info(f"用例数据关联变量 {var_key} 替换完成")
                    except Exception as e:
                        raise VariableError(f"用例数据关联变量 {var_key} 替换失败: {e}")
                else:
                    raise VariableError(
                        f'用例数据关联变量替换失败，临时变量池不存在变量: "{var_key}"'
                    )

        log.info("关联测试用例变量替换完毕")
        # TODO: https://github.com/StKali/cache3/issues/18
        if var_keys:
            log.info("自动清理关联变量中...")
            for var_key in var_keys:
                variable_cache.delete(var_key, tag="relate_testcase")

        dict_target = json.loads(str_target)

        return dict_target

    @staticmethod
    def teardown_var_extract(response: dict, extract: dict, env: str) -> None:
        """
        后置参数提取

        :param response:
        :param extract:
        :param env:
        :return:
        """
        log.info(f'🔸 执行变量置换: {extract["key"]} = {extract["jsonpath"]}')

        key = extract["key"]
        set_type = extract.get("type", "")
        json_path = extract["jsonpath"]
        record_variables(json_path, response, key, set_type, env)

    def extract_json_value(self, data: dict, jsonpath_expr: str):
        """
        使用 jsonpath_expr 从 data 中提取匹配值，返回第一个匹配结果

        :param data: dict，响应 JSON
        :param jsonpath_expr: str，jsonpath 表达式，比如 '$.json.code'
        :return: 匹配值或 None
        """
        try:
            jsonpath_expr_obj = jsonpath_parse(jsonpath_expr)
            if not isinstance(data, dict):
                log.error("传入的 data 不是 dict 类型")
                return None
            results = jsonpath_expr_obj.find(data)
            matches = (
                [match.value for match in results]
                if hasattr(results, "__iter__")
                else []
            )
            if matches:
                return matches[0]
        except Exception as e:
            log.error(f"jsonpath 解析失败: {e}")
        return None


var_extractor = VarsExtractor()
